import time
import os
import re
from nanchangproject import settings
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from .models import Drivinginfo
from .serializers import DrivinginfoSerializer
from .models import DrivingMode
from .serializers import DrivingModeSerializer
from .models import LogisticsMode
from .serializers import LogisticsModeSerializer
from .serializers import GPSSerializer
from .models import BasicInfo
from .models import DataFields
from .models import DataDescription
from .serializers import DataDescriptionSerializer
from .serializers import BasicInfoSerializer
from .serializers import FuelInfoSerializer
from .models import WorkingPlace
from .serializers import WorkingPlaceSerializer
from django.db.models import Avg
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
from utils.Load.load_analysis_2 import LoadModel
from .models import LoadFile
from .serializers import LoadFileSerializer
from utils.Speed.speed_analysis import SpeedAnalysisModel
from .models import LoadPicture
from .serializers import LoadPictureSerializer


# 使用CBV编写API接口
class DrivinginfoList(APIView):
    permission_classes = (IsAuthenticated,)  # 全局权限已经设置

    def get(self, request):
        """
        :param request:
        :return:
        """
        queryset = Drivinginfo.objects.all()[:10]
        s = DrivinginfoSerializer(instance=queryset, many=True)  # many表示query中有多个对象
        return Response(s.data, status=status.HTTP_200_OK)

    def post(self, request):
        """
        :param request:
        :return:
        """
        s = DrivinginfoSerializer(data=request.data)  # data是前端传递的数据，return前进行校验
        if s.is_valid():
            s.save()
            print(type(request.data), type(s.data))
            return Response(data=s.data, status=status.HTTP_201_CREATED)
        return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)


class DrivinginfoDetail(APIView):
    permission_classes = (IsAuthenticated,)

    @staticmethod
    def get_object(pk):
        """
        :param pk: 数据id
        :return:
        """
        # 该函数返回结果可供下面三个函数使用
        try:
            return Drivinginfo.objects.get(pk=pk)
        except Drivinginfo.DoesNotExist:
            return None

    def get(self, request, pk):
        """
        :param request:
        :param pk:
        :return:
        """
        obj = self.get_object(pk=pk)
        print(request.user)
        if not obj:
            return Response(data={"msg": "没有此车辆信息"}, status=status.HTTP_404_NOT_FOUND)
        else:
            s = DrivinginfoSerializer(instance=obj)
            return Response(s.data, status=status.HTTP_200_OK)

    def put(self, request, pk):
        """
        :param request:
        :param pk:
        :return:
        """
        obj = self.get_object(pk=pk)
        if not obj:
            return Response(data={"msg": "没有此车辆信息"}, status=status.HTTP_404_NOT_FOUND)
        else:
            s = DrivinginfoSerializer(instance=obj, data=request.data)
            if s.is_valid():
                s.save()
                return Response(data=s.data, status=status.HTTP_201_CREATED)
            else:
                return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        """
        :param request:
        :param pk:
        :return:
        """
        obj = self.get_object(pk=pk)
        if not obj:
            return Response(data={"mag": "没有此车辆信息"}, status=status.HTTP_404_NOT_FOUND)
        else:
            obj.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)


class DrivingModeList(APIView):
    permission_classes = (IsAuthenticated,)  # 全局权限已经设置

    def get(self, request):
        """
        :param request:
        :return:
        """
        queryset = DrivingMode.objects.all()
        s = DrivingModeSerializer(instance=queryset, many=True)  # many表示query中有多个对象
        return Response(s.data, status=status.HTTP_200_OK)


class LogisticsModeList(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        """
        :param request:
        :return:
        """
        queryset = LogisticsMode.objects.all()
        s = LogisticsModeSerializer(instance=queryset, many=True)
        return Response(s.data, status=status.HTTP_200_OK)


class DrivingAndLogisticsMode(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        """
        :param request:
        :return:
        """
        vin_list = BasicInfo.objects.all()
        # vin_list中可能部分车辆没有进行驾驶风格和物流模式的识别
        ret_list = []
        for index, item in enumerate(vin_list):
            try:
                my_drivingmode = item.drivingmode
            except DrivingMode.DoesNotExist:
                continue
            try:
                my_logisticsmode = item.logisticsmode
            except LogisticsMode.DoesNotExist:
                continue
            info_item = {
                'index': index,
                'vin': item.vin,
                'driving_mode': my_drivingmode.driving_mode,
                'car_speed_mean': my_drivingmode.car_speed_mean,
                'car_speed_sd': my_drivingmode.car_speed_sd,
                'acceleration_sd': my_drivingmode.acceleration_sd,
                'overspeed_proportion': my_drivingmode.overspeed_proportion,
                'rapid_acceleration_proportion': my_drivingmode.rapid_acceleration_proportion,
                'engine_speed_mean': my_drivingmode.engine_speed_mean,
                'engine_speed_sd': my_drivingmode.engine_speed_sd,
                'logistics_mode': my_logisticsmode.logistics_mode,
                'radius_of_gyration': my_logisticsmode.radius_of_gyration,
                'daily_travel_location': my_logisticsmode.daily_travel_location,
                'motion_entropy': my_logisticsmode.motion_entropy,
                'daily_driving_distance': my_logisticsmode.daily_driving_distance,
                'daily_driving_time': my_logisticsmode.daily_driving_time,
            }
            ret_list.append(info_item)
        return Response(ret_list, status=status.HTTP_200_OK)


class GPSList(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        try:
            vin = self.request.query_params.get('vin')
            queryset = Drivinginfo.objects.filter(vin=vin)
            count = queryset.count()
            id_start = queryset.first().id
            id_list = [i for i in range(id_start, id_start + count) if i % 30 == 1]
            track = queryset.filter(id__in=id_list)
            s = GPSSerializer(instance=track, many=True)
            return Response(s.data, status=status.HTTP_200_OK)
        except Drivinginfo.DoesNotExist:
            return Response(data={"msg": "没有此车辆信息"}, status=status.HTTP_404_NOT_FOUND)


class WorkingPlaceList(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        try:
            # queryset = WorkingPlace.objects.all()
            # s = WorkingPlaceSerializer(instance=queryset, many=True)
            working_place = WorkingPlace.objects.all()
            ret_list = []
            for item in working_place:
                info_item = {
                    'vin': item.basic_info.vin,
                    'workplace': item.nearest_site,
                    'work': item.max_tag,
                    'word': item.max_word,
                    'lng': item.longitude,
                    'lat': item.latitude,
                }
                ret_list.append(info_item)
            return Response(ret_list, status=status.HTTP_200_OK)
        except WorkingPlace.DoesNotExist:
            return Response(data={"msg": "没有此车辆信息"}, status=status.HTTP_404_NOT_FOUND)


class DataDescriptionInfo(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        try:
            vin = self.request.query_params.get('vin')
            field_name = self.request.query_params.get('field_name')
            # 由于DataDescription会更新，因此同一个field_name和vin会有多条数据
            info = DataDescription.objects.filter(field__field_name=field_name, basic_info__vin=vin)
            # print(info)  # 它居然不报错，而是返回None
            if not info:
                return Response(data={"msg": "没有此车辆的该字段信息"}, status=status.HTTP_404_NOT_FOUND)
            s = DataDescriptionSerializer(instance=info, many=True)
            return Response(s.data, status=status.HTTP_200_OK)
        except DataDescription.DoesNotExist:
            return Response(data={"msg": "没有此车辆的该字段信息"}, status=status.HTTP_404_NOT_FOUND)


class BasicInfoList(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        try:
            info = BasicInfo.objects.all()
            s = BasicInfoSerializer(instance=info, many=True)
            return Response(s.data, status=status.HTTP_200_OK)
        except BasicInfo.DoesNotExist:
            return Response(data={"msg": "没有此VIN码的信息"}, status=status.HTTP_404_NOT_FOUND)


class FuelInfoList(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        try:
            info = Drivinginfo.objects.all()[:20]
            s = FuelInfoSerializer(instance=info, many=True)
            return Response(s.data, status=status.HTTP_200_OK)
        except Drivinginfo.DoesNotExist:
            return Response(data={"msg": "没有此VIN码的信息"}, status=status.HTTP_404_NOT_FOUND)


# @method_decorator(decorator=cache_page(timeout=600, cache='default'), name='get')
class FuelInfoDetail(APIView):
    permission_classes = (IsAuthenticated,)

    def get(self, request):
        try:
            # 两个参数，一个vin，一个date，2023或2023-02或2023-02-09
            vin = self.request.query_params.get('vin')
            date = self.request.query_params.get('date')
            date_list = date.split('-')  # 一个列表
            start_time = time.perf_counter()
            this_vin_data = Drivinginfo.objects.filter(vin=vin)
            if len(date_list) == 1:
                # 某一年的油耗变化
                data = this_vin_data.filter(data_time__year=date_list[0])
                if not data.exists():
                    return Response(data={"msg": "没有此VIN码的该年信息"}, status=status.HTTP_404_NOT_FOUND)
                result = []
                for month in range(1, 13):
                    month_data = data.filter(data_time__month=month)
                    if not month_data.exists():
                        # 该数据不存在
                        result.append(None)
                        continue
                    month_data_mean = month_data.aggregate(Avg('fuel_consumption_pre'))  # 用Avg方法速度较慢
                    avg_result = month_data_mean['fuel_consumption_pre__avg']
                    result.append(avg_result)
                print(f'耗时{time.perf_counter() - start_time}')
                return Response(data=result, status=status.HTTP_200_OK)

            elif len(date_list) == 2:
                # 某个月的油耗变化
                data = this_vin_data.filter(data_time__year=date_list[0]).filter(data_time__month=date_list[1])
                if not data.exists():
                    return Response(data={"msg": "没有此VIN码的该年该月信息"}, status=status.HTTP_404_NOT_FOUND)
                result = []
                for day in range(1, 31):
                    day_data = data.filter(data_time__day=day)
                    # 由于django是惰性查询，因此该查询会执行30次
                    # 使用values取部分字段证明对查询速度的影响不大
                    if not day_data.exists():
                        result.append(None)
                        continue
                    day_data_mean = day_data.aggregate(Avg('fuel_consumption_pre'))
                    avg_result = day_data_mean['fuel_consumption_pre__avg']
                    result.append(avg_result)
                print(f'耗时{time.perf_counter() - start_time}')
                return Response(data=result, status=status.HTTP_200_OK)
            elif len(date_list) == 3:
                # 某一天的油耗变化
                data = this_vin_data.filter(data_time__year=date_list[0]).filter(
                    data_time__month=date_list[1]).filter(data_time__day=date_list[2])
                if not data.exists():
                    return Response(data={"msg": "没有此VIN码的该年该月该日信息"}, status=status.HTTP_404_NOT_FOUND)
                result = []
                for hour in range(0, 24):
                    hour_data = data.filter(data_time__hour=hour)
                    if not hour_data.exists():
                        result.append(None)
                        continue
                    hour_data_mean = hour_data.aggregate(Avg('fuel_consumption_pre'))
                    avg_result = hour_data_mean['fuel_consumption_pre__avg']
                    result.append(avg_result)
                print(f'耗时{time.perf_counter() - start_time}')
                return Response(data=result, status=status.HTTP_200_OK)
            else:
                return Response(data={"msg": "非法日期"}, status=status.HTTP_404_NOT_FOUND)

        except Drivinginfo.DoesNotExist:
            return Response(data={"msg": "没有此VIN码的信息"}, status=status.HTTP_404_NOT_FOUND)


class AllVehicleList(APIView):
    """
    用于查看数据库中已有车辆数量及分布的接口
    """
    permission_classes = (IsAuthenticated,)

    @staticmethod
    def get(request):
        all_data_count = Drivinginfo.objects.all().count()  # 统计总数据量
        vehicle_count = BasicInfo.objects.all().count()  # 总汽车数
        china_provinces = [
            "北京市", "天津市", "河北省", "山西省", "内蒙古自治区",
            "辽宁省", "吉林省", "黑龙江省", "上海市", "江苏省",
            "浙江省", "安徽省", "福建省", "江西省", "山东省",
            "河南省", "湖北省", "湖南省", "广东省", "广西壮族自治区",
            "海南省", "重庆市", "四川省", "贵州省", "云南省",
            "西藏自治区", "陕西省", "甘肃省", "青海省", "宁夏回族自治区",
            "新疆维吾尔自治区", "台湾省", "香港特别行政区", "澳门特别行政区"
        ]
        find_list = {}
        for province in china_provinces:
            this_province_count = BasicInfo.objects.filter(province=province).count()
            if this_province_count > 0:
                find_list[province] = this_province_count
        # 计算总和
        total = sum(find_list[d] for d in find_list)
        # 计算每个省份的百分比并排序
        sorted_data = dict(sorted(find_list.items(), key=lambda item: item[1], reverse=True))
        # 计算总和
        total = sum(sorted_data.values())
        # 计算百分比并存储到新的字典中
        percentage_data = {province: (value / total) * 100 for province, value in sorted_data.items()}

        return Response({
            '总数据量': all_data_count,
            '总汽车数': vehicle_count,
            '省份统计': percentage_data
        }, status=status.HTTP_200_OK)


class LoadReceiveFile(APIView):
    """
    用于进行载荷文件上传的接口
    """

    @staticmethod
    def post(request):
        file_obj = request.FILES.get('file')
        uid = request.POST.get('uid')
        this_upload = LoadFile(
            uid=uid,
            file_name=file_obj.name,
            file_data=file_obj,
        )
        this_upload.save()
        return Response(data={"msg": file_obj.name + "文件上传成功"}, status=status.HTTP_200_OK)


class LoadManageFile(APIView):
    """
    用于管理载荷文件的接口，包括查看和删除
    """

    @staticmethod
    def get(request):
        try:
            load_file_list = LoadFile.objects.all()
            s = LoadFileSerializer(instance=load_file_list, many=True)
            return Response(s.data, status=status.HTTP_200_OK)
        except LoadFile.DoesNotExist:
            return Response(data={"msg": "无法加载载荷文件信息"}, status=status.HTTP_404_NOT_FOUND)

    @staticmethod
    def delete(request):
        try:
            uid = request.query_params.get('uid')
            delete_file = LoadFile.objects.get(uid=uid)
            # 获得文件路径
            file_path = delete_file.file_data.path
            print(f'路径为{file_path}，该文件删除成功')
            # 进行文件删除
            try:
                os.remove(file_path)
            except FileNotFoundError:
                print(f'路径为{file_path}，该文件不存在')
            # 将数据库中的信息删除
            delete_file.delete()

            return Response(data={"msg": "载荷文件删除成功"}, status=status.HTTP_204_NO_CONTENT)
        except LoadFile.DoesNotExist:
            return Response(data={"msg": "没有该载荷文件"}, status=status.HTTP_404_NOT_FOUND)


class LoadManagePicture(APIView):
    """
    用于管理载荷预测图的接口，包括查看和删除
    """

    @staticmethod
    def get(request):
        try:
            load_picture_list = LoadPicture.objects.all()
            s = LoadPictureSerializer(instance=load_picture_list, many=True)
            return Response(s.data, status=status.HTTP_200_OK)
        except LoadPicture.DoesNotExist:
            return Response(data={"msg": "无法加载载荷预测图列表"}, status=status.HTTP_404_NOT_FOUND)

    @staticmethod
    def delete(request):
        try:
            pic_name = request.query_params.get('picture_name')
            delete_file = LoadPicture.objects.get(picture_name=pic_name)
            # 获得文件路径
            file_path = settings.MEDIA_ROOT + '/load_pictures/' + pic_name
            # 进行文件删除
            try:
                os.remove(file_path)
                print(f'路径为{file_path}，该文件删除成功')
            except FileNotFoundError:
                print(f'路径为{file_path}，该文件不存在')
            # 将数据库中的信息删除
            delete_file.delete()
            return Response(data={"msg": "载荷预测图删除成功"}, status=status.HTTP_204_NO_CONTENT)
        except LoadPicture.DoesNotExist:
            return Response(data={"msg": "没有该载荷预测图"}, status=status.HTTP_404_NOT_FOUND)


class LoadAnalysis(APIView):
    """
    用于进行载荷计算的接口
    """

    def post(self, request):
        data = request.data
        ig = data.get('ig')  # 主减速比
        torque_max = data.get('torque_max')  # 最大扭矩
        windward_area = data.get('windward_area')  # 车辆迎风面积
        air_density = data.get('air_density')  # 空气密度
        air_coefficient = data.get('air_coefficient')  # 空气阻力系数
        road_coefficient = data.get('road_coefficient')  # 滚阻系数
        tire_radius = data.get('tire_radius')  # 轮胎滚动半径
        ratio_r = data.get('ratio_r')  # 倒挡转动比，以下依次类推
        ratio_1 = data.get('ratio_1')
        ratio_2 = data.get('ratio_2')
        ratio_3 = data.get('ratio_3')
        ratio_4 = data.get('ratio_4')
        ratio_5 = data.get('ratio_5')
        ratio_6 = data.get('ratio_6')
        uid = data.get('uid')

        my_load = LoadModel(ig=float(ig),
                            torque_max=float(torque_max),
                            windward_area=float(windward_area),
                            air_density=float(air_density),
                            air_coefficient=float(air_coefficient),
                            road_coefficient=float(road_coefficient),
                            tire_radius=float(tire_radius),
                            ratio_r=float(ratio_r),
                            ratio_1=float(ratio_1),
                            ratio_2=float(ratio_2),
                            ratio_3=float(ratio_3),
                            ratio_4=float(ratio_4),
                            ratio_5=float(ratio_5),
                            ratio_6=float(ratio_6))
        my_file = LoadFile.objects.get(uid=uid)
        file_name = my_file.file_data.name.split('/')[-1]
        file_path = my_file.file_data.path  # 这是个完整的路径
        print('文件名', file_name)
        print('文件路径', file_path)
        # 进行载荷计算
        # time, engine_rpm, v_kmph, v_mps, Torque = my_load.read_csv_actual(file_path)
        time, engine_rpm, v_kmph, v_mps, Torque = my_load.read_csv_new(file_path)
        # 运行主循环
        state_dict, final_covariance = my_load.main_loop_actual(time, engine_rpm, v_kmph, v_mps, Torque)
        # state_dict是一个三元组列表，其中第二个是载荷预测值
        aim_mass = [state_dict[i][1] for i in range(len(state_dict))]
        # 计算平均载荷
        mean_mass = round(sum(aim_mass) / len(aim_mass), 4)
        print('载荷数据量', len(aim_mass), '载荷均值', mean_mass)

        # 进行载荷计算图的存储
        pic_path_is_exist = True
        plot_num = 1
        basic_path = settings.MEDIA_ROOT + '/load_pictures/' + file_name.split('.')[0]
        picture_path = basic_path + '_plot' + '.png'
        while pic_path_is_exist:
            picture_path = basic_path + '_plot_' + str(plot_num) + '.png'
            if os.path.exists(picture_path):
                plot_num += 1
            else:
                pic_path_is_exist = False
                my_load.plot_data(state_dict, picture_path)

        # 将载荷预测图信息放在数据库中
        plot_name = picture_path.split('/')[-1]
        load_picture_model = LoadPicture(picture_name=plot_name)
        load_picture_model.save()

        # 进行平均速度的计算
        speed_model = SpeedAnalysisModel()
        mean_speed, mean_speed_no_stop = speed_model.speed_analyze(file_path)
        return Response(data={"msg": plot_name,
                              "mean_load": mean_mass,
                              "mean_speed": mean_speed,
                              "mean_speed_no_stop": mean_speed_no_stop},
                        status=status.HTTP_200_OK)
